﻿using System.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace EFCore.EntityFrameworkExtensions
{
    public static class QueryExpression
    {
        /// <summary>
        /// 创建Expression
        /// </summary>
        /// <param name="param"></param>
        /// <param name="entities"></param>
        /// <param name="fromIndex"></param>
        /// <returns></returns>
        public static Expression CreateExpressionDelegate(ParameterExpression param, IList<QueryEntity> entities, int fromIndex = 0)
        {
            var first_entitie = entities[fromIndex];
            var left_expression = CreateExpressionDelegate(param, first_entitie);
            for (var i = fromIndex + 1; i < entities.Count; i++)
            {
                var entitiy = entities[i];
                Expression right_expression = null;
                if ("OR".Equals(entitiy.LogicalOperator, StringComparison.OrdinalIgnoreCase))
                {
                    right_expression = CreateExpressionDelegate(param, entities, i);
                    left_expression = AndOrExpression(entitiy.LogicalOperator, left_expression, right_expression);
                    break;
                }
                else
                {
                    right_expression = CreateExpressionDelegate(param, entitiy);
                    left_expression = AndOrExpression(entitiy.LogicalOperator, left_expression, right_expression);
                }
            }
            return left_expression;
        }

        /// <summary>
        /// 创建 Expression
        /// </summary>
        private static Expression CreateExpressionDelegate(ParameterExpression param, QueryEntity entity)
        {
            Expression key = param;
            var entityKey = entity.Key.Trim();
            var tableNameAndField = entityKey.Split('.');
            foreach (var fieldName in tableNameAndField)
            {
                key = Expression.Property(key, fieldName);
            }

            Expression value = Expression.Constant(ParseType(param.Type, entity));
            if (key.Type != value.Type)
            {
                value = Expression.Convert(value, key.Type);
            }
            Expression body = CreateExpression(key, value, entity.Operator);

            if (entity.ChildQueryEntitys?.Count > 0)
            {
                var child_expression = CreateExpressionDelegate(param, entity.ChildQueryEntitys);
                body = AndOrExpression(entity.ChildQueryEntitys[0].LogicalOperator, body, child_expression);
            }
            return body;
        }

        private static Expression AndOrExpression(string LogicalOperator, Expression left_expression, Expression child_expression)
        {
            if ("OR".Equals(LogicalOperator, StringComparison.OrdinalIgnoreCase))
            {
                left_expression = Expression.OrElse(left_expression, child_expression);
            }
            else
            {
                left_expression = Expression.AndAlso(left_expression, child_expression);
            }
            return left_expression;
        }


        /// <summary>
        /// 属性类型转换
        /// </summary>
        /// <param name="entity">查询实体</param>
        /// <returns></returns>
        private static object ParseType(Type TSourceType, QueryEntity entity)
        {
            var propertyType = TSourceType;

            var tableNameAndField = entity.Key.Split('.');
            foreach (var field in tableNameAndField)
            {
                var property = propertyType.GetProperty(field);
                if (property == null)
                {
                    throw new ArgumentException("Key错误，无法找到对应的属性");
                }
                propertyType = property.PropertyType;
            }

            if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                var valueType = propertyType.GenericTypeArguments[0];
                var value = Convert.ChangeType(entity.Value, valueType);
                return value;
            }
            return Convert.ChangeType(entity.Value, propertyType);
        }

        /// <summary>
        /// 创建 Expression
        /// </summary>
        private static Expression CreateExpression(Expression left, Expression value, OperatorEnum entityOperator)
        {
            return entityOperator switch
            {
                OperatorEnum.Equals => Expression.Equal(left, value),
                OperatorEnum.NotEqual => Expression.NotEqual(left, value),
                OperatorEnum.Contains => Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value),
                OperatorEnum.StartsWith => Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), value),
                OperatorEnum.EndsWith => Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), value),
                OperatorEnum.Greater => Expression.GreaterThan(left, value),
                OperatorEnum.GreaterEqual => Expression.GreaterThanOrEqual(left, value),
                OperatorEnum.Less => Expression.LessThan(left, value),
                OperatorEnum.LessEqual => Expression.LessThanOrEqual(left, value),
                _ => Expression.Equal(left, value),
            };
        }
    }
}
